package com.vcc.web.controller;

import cn.dev33.satoken.secure.SaSecureUtil;
import cn.dev33.satoken.secure.totp.SaTotpUtil;
import cn.hutool.core.lang.Validator;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.json.JSONUtil;
import com.vcc.core.annotations.ROReject;
import com.vcc.core.annotations.RestPack;
import com.vcc.core.exception.ProgramException;
import com.vcc.core.util.IpUtil;
import com.vcc.core.util.RedisCustomizeUtils;
import com.vcc.core.util.RegexUtil;
import com.vcc.data.entity.User;
import com.vcc.service.client.dto.email.SendEmailDto;
import com.vcc.service.service.ClientParamService;
import com.vcc.service.service.ComptrollerService;
import com.vcc.service.service.UserService;
import com.vcc.web.core.component.SmsComponent;
import com.vcc.web.core.util.UserUtil;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.apache.catalina.util.URLEncoder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.nio.charset.Charset;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

@RestController
@RequestMapping("email")
public class EmailController {

    @Resource
    private UserService userService;
    @Resource
    private SmsComponent smsComponent;
    @Resource
    private ComptrollerService comptrollerService;
    @Resource
    private ClientParamService clientParamService;

    @Value("${spring.profiles.active}")
    private String activeProfile;

    @RestPack
    @ROReject
    @PostMapping("sendEmail")
    public String sendEmail(
            @RequestBody @Validated SendEmailDto sendEmailDto,
            HttpServletRequest request,
            HttpServletResponse response) {
        if (RegexUtil.checkStringIsNull(sendEmailDto.getChannel())) {
            throw new ProgramException("渠道不能为空");
        }
        String code = UUID.randomUUID().toString().replace("-", "");
        if ("unLoginForgot".equals(sendEmailDto.getChannel())) {
            if (RegexUtil.checkObjectIsNull(sendEmailDto.getEmail())) {
                throw new ProgramException("邮箱不能为空");
            }
            if (!Validator.isEmail(sendEmailDto.getEmail())) {
                throw new ProgramException("邮箱格式不正确");
            }
            if (RegexUtil.checkStringIsNull(sendEmailDto.getGoogleCode())) {
                throw new ProgramException("谷歌验证码不能为空");
            }
            User user = userService.getByEmail(sendEmailDto.getEmail());
            if (!user.getHasBindGoogle()) {
                throw new ProgramException("您当前尚未绑定谷歌验证，如果需要重置密码，请联系客服经理");
            }
            if (!SaTotpUtil.validateTOTP(SaSecureUtil.aesDecrypt(user.getSalt(), user.getGoogleSecret()), sendEmailDto.getGoogleCode(), 1) && activeProfile.equals("prod")) {
                Long increase = RedisCustomizeUtils.increase("verify:error:count:" + user.getId(), 1L, 60L);
                if (increase >= 5) {
                    userService.updateStatusById(user.getId(), 2);
                    String clientIp = IpUtil.getIp(request);
                    comptrollerService.block(clientIp, user.getId(), 0);
                    throw new ProgramException("验证失败次数过多，账户已被锁定");
                }
                throw new ProgramException("谷歌验证码不正确");
            }
            RedisCustomizeUtils.set("unLoginForgot:" + code, user.getId().toString(), 1L, TimeUnit.DAYS);
            smsComponent.sendUpdatePasswordEmail(user.getEmail(), encryptUrl(("unLoginForgot-" + code)));
        } else if (sendEmailDto.getChannel().equals("register")) {
            if (RegexUtil.checkObjectIsNull(sendEmailDto.getEmail())) {
                throw new ProgramException("邮箱不能为空");
            }
            if (!Validator.isEmail(sendEmailDto.getEmail())) {
                throw new ProgramException("邮箱格式不正确");
            }
            String forbidRegisterEmailSuffix = clientParamService.getValueByCodeAndUserIdOrDefaultValue("FORBID_REGISTER_EMAIL_SUFFIX", null, "['@qq.com','@163.com']");
            List<String> list = JSONUtil.toList(forbidRegisterEmailSuffix, String.class);
            if (list.stream().anyMatch(it -> sendEmailDto.getEmail().contains(it))) {
                throw new ProgramException("当前邮箱后缀不允许注册");
            }
            if (userService.existsByAccount(sendEmailDto.getEmail())) {
                throw new ProgramException("邮箱已被注册");
            }
            code = RandomUtil.randomNumbers(6);
            Boolean tempSendFlag = RedisCustomizeUtils.setIfAbsent(
                    "register:" + sendEmailDto.getEmail().toLowerCase(),
                    code,
                    5L,
                    TimeUnit.MINUTES
            );
            if (!tempSendFlag) {
                Long expire = RedisCustomizeUtils.getExpire("register:" + sendEmailDto.getEmail().toLowerCase());
                throw new ProgramException("请勿频繁发送验证码，请" + expire + "秒后再试", expire);
            }
            if ("dev".equals(activeProfile)){
                return code;
            }
            smsComponent.sendAccountCodeEmail(sendEmailDto.getEmail(), code);
        }else if (sendEmailDto.getChannel().equals("create")) {
            if (RegexUtil.checkObjectIsNull(sendEmailDto.getEmail())) {
                throw new ProgramException("邮箱不能为空");
            }
            if (!Validator.isEmail(sendEmailDto.getEmail())) {
                throw new ProgramException("邮箱格式不正确");
            }
            String forbidRegisterEmailSuffix = clientParamService.getValueByCodeAndUserIdOrDefaultValue("FORBID_REGISTER_EMAIL_SUFFIX", null, "['@qq.com','@163.com']");
            Long clientCreateSubAccountMaxCount =
                    Long.valueOf(clientParamService.getValueByCodeAndUserIdOrDefaultValue("CLIENT_CREATE_SUB_ACCOUNT_MAX_COUNT", UserUtil.getId(), "5"));
            if (userService.countByParentId(UserUtil.getId()) >= clientCreateSubAccountMaxCount) {
                throw new ProgramException("当前用户创建子账户数量已达上限，请联系管理员");
            }
            List<String> list = JSONUtil.toList(forbidRegisterEmailSuffix, String.class);
            if (list.stream().anyMatch(it -> sendEmailDto.getEmail().contains(it))) {
                throw new ProgramException("当前邮箱后缀不允许注册");
            }
            if (userService.existsByAccount(sendEmailDto.getEmail())) {
                throw new ProgramException("邮箱已被注册");
            }
            code = RandomUtil.randomNumbers(6);
            Boolean tempSendFlag = RedisCustomizeUtils.setIfAbsent(
                    "create:" + sendEmailDto.getEmail().toLowerCase(),
                    code,
                    5L,
                    TimeUnit.MINUTES
            );
            if (!tempSendFlag) {
                Long expire = RedisCustomizeUtils.getExpire("create:" + sendEmailDto.getEmail().toLowerCase());
                throw new ProgramException("请勿频繁发送验证码，请" + expire + "秒后再试", expire);
            }
            if ("dev".equals(activeProfile)){
                return code;
            }
            smsComponent.sendAccountCodeEmail(sendEmailDto.getEmail(), code);
        } else {
            throw new ProgramException("渠道错误");
        }
        return "";
    }

    private String encryptUrl(String url) {
        return new URLEncoder().encode(url, Charset.defaultCharset());
    }
}
